﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using BesAsm.Stormwater.TimeSeries;

namespace BesAsm.Stormwater.Swmm5Results
{
  public class PostProcessor
  {
    public void ExtractMaximumValues(string inputFile, string outputFile)
    {
      StreamReader reader = new StreamReader(inputFile);
      StreamWriter writer = new StreamWriter(outputFile);

      Dictionary<string, TimeSeries<double>> timeSeriesies = new Dictionary<string, TimeSeries<double>>();

      List<string> entities = new List<string>();
      List<string> entityTypes = new List<string>();

      string line = reader.ReadLine();
      string[] tokens = line.Split(',');
      int entityCount = 0;
      int entityTypeCount = 0;

      foreach (string token in tokens.Skip(1))
      {
        string entityId = token.Split('_')[0];
        string entityType = token.Split('_')[1];

        if (!entities.Contains(entityId))
        {
          entities.Add(entityId);
          entityCount++;
        }
        if (!entityTypes.Contains(entityType))
        {
          entityTypes.Add(entityType);
          entityTypeCount++;
        }
      }

      double[,] maxResults = new double[entityCount, entityTypeCount];
      for (int i = 0; i < entityCount; i++)
        for (int j = 0; j < entityTypeCount; j++)
          maxResults[i, j] = double.MinValue;

      while ((line = reader.ReadLine()) != null)
      {
        tokens = line.Split(',').Skip(1).ToArray();

        for (int i = 0; i < entityCount; i++)
          for (int j = 0; j < entityTypeCount; j++)
          {
            int currentIndex = i * entityTypeCount + j;
            double value = double.Parse(tokens[currentIndex]);
            maxResults[i, j] = Math.Max(maxResults[i, j], value);
          }
      }
      reader.Close();

      writer.Write("Entity,");
      foreach (string s in entityTypes)
      {
        writer.Write(s + ",");
      }
      writer.WriteLine();

      for (int i = 0; i < entityCount; i++)
      {
        writer.Write(entities[i] + ",");
        for (int j = 0; j < entityTypeCount; j++)
        {
          writer.Write(maxResults[i, j] + ",");
        }
        writer.WriteLine();
      }
      writer.Close();
    }

    private static int[] sumRanges = { 60, 120, 180, 360, 720, 1440 };
    public void ProcessRunningAverages(StreamReader reader, StreamWriter writer)
    {
      double eventThreshold;
      //eventThreshold = 1.79; //value for Stephens Creek at I-5 culvert
      eventThreshold = 3.36; //value for Stephens Creek near mouth;
      eventThreshold = 0;

      int interEventTime = 1440;
      int timeStep = 60;

      string line;// = string.Empty;
      string[] tokens;

      DateTime dt = DateTime.MinValue;
      double value = 0;

      Dictionary<DateTime, RunningSum> runningSums = new Dictionary<DateTime, RunningSum>();
      TimeSeries<double> timeSeries = new TimeSeries<double>();
      string headerLine = reader.ReadLine();

      while ((line = reader.ReadLine()) != null)
      {
        tokens = line.Split(',');
        dt = DateTime.Parse(tokens[0]);
        value = double.Parse(tokens[1]);
        timeSeries.Add(dt, value);
      }

      int recordCount = timeSeries.Count;
      for (int i = 0; i <= (interEventTime / timeStep); i++)
      {
        dt = dt.AddMinutes(timeStep);
        timeSeries.Add(dt, 0);
      }

      double[] array = timeSeries.Values.ToArray();

      double maxValueWithinIntereventTime = 0;
      bool inEvent = false;
      RunningSum currentRunningSum;
      DateTime? currentEventStart = null;

      dt = timeSeries.Keys.ElementAt(0);
      for (int i = 0; i < recordCount; i++)
      {
        maxValueWithinIntereventTime = 0;
        currentRunningSum = new RunningSum();
        currentRunningSum.CurrentValue = array[i];

        maxValueWithinIntereventTime = 0;
        for (int j = 0; j < interEventTime / timeStep - 1; j++)
        {
          maxValueWithinIntereventTime = Math.Max(maxValueWithinIntereventTime, array[i + j]);
        }

        if (array[i] > eventThreshold && !inEvent)
        {
          inEvent = true;
          currentEventStart = timeSeries.Keys.ElementAt(i);
        }
        else if (maxValueWithinIntereventTime <= eventThreshold && inEvent)
        {
          inEvent = false;
          currentEventStart = null;
        }

        if (inEvent)
          currentRunningSum.EventStart = currentEventStart;
        else
          currentRunningSum.EventStart = null;

        int k = 0;
        value = 0;
        foreach (int sumRange in sumRanges)
        {
          for (int l = k / timeStep; l < sumRange / timeStep; l++)
          {
            value += array[i + l];
          }
          currentRunningSum.Totals[sumRange] = value / (sumRange / timeStep);
          k = sumRange;
        }
        runningSums.Add(dt, currentRunningSum);
        dt = dt.AddMinutes(timeStep);

      }

      IEnumerable<IGrouping<DateTime?, RunningSum>> query =
        runningSums.GroupBy(p => p.Value.EventStart, p => p.Value);

      writer.Write("Date,");
      foreach (int range in sumRanges)
        writer.Write(range + ",");
      writer.WriteLine();

      foreach (IGrouping<DateTime?, RunningSum> grouping in query)
      {
        if (!grouping.Key.HasValue)
          continue;

        writer.Write(grouping.Key.Value.ToString(@"MM/dd/yyyy HH:mm:ss") + ",");

        foreach (int range in sumRanges)
        {
          double sum = grouping.Max(p => p.Totals[range]);
          writer.Write(sum + ",");
        }
        writer.WriteLine();
      }
      reader.Close();
      writer.Close();

      return;
    }
    
    public void ProcessRunningSums(StreamReader reader, StreamWriter writer)
    {
      int interEventTime = 1440;
      int timeStep = 15;

      string line;// = string.Empty;
      string[] tokens;

      DateTime dt = DateTime.MinValue;
      double value = 0;

      Dictionary<DateTime, RunningSum> runningSums = new Dictionary<DateTime, RunningSum>();
      TimeSeries<double> timeSeries = new TimeSeries<double>();
      string headerLine = reader.ReadLine();

      while ((line = reader.ReadLine()) != null)
      {
        tokens = line.Split(',');
        dt = DateTime.Parse(tokens[0]);
        value = double.Parse(tokens[1]);
        timeSeries.Add(dt, value);
      }

      int recordCount = timeSeries.Count;
      for (int i = 0; i <= (interEventTime / timeStep); i++)
      {
        dt = dt.AddMinutes(timeStep);
        timeSeries.Add(dt, 0);
      }

      double[] array = timeSeries.Values.ToArray();

      double maxValueWithinIntereventTime = 0;
      bool inEvent = false;
      RunningSum currentRunningSum;
      DateTime? currentEventStart = null;

      DateTime[] dateTimes = timeSeries.Keys.ToArray();

      dt = timeSeries.Keys.ElementAt(0);
      for (int i = 0; i < recordCount; i++)
      {
        maxValueWithinIntereventTime = 0;
        currentRunningSum = new RunningSum();
        currentRunningSum.CurrentValue = array[i];

        maxValueWithinIntereventTime = 0;
        for (int j = 0; j < interEventTime / timeStep - 1; j++)
        {
          maxValueWithinIntereventTime = Math.Max(maxValueWithinIntereventTime, array[i + j]);
        }

        if (!inEvent && array[i] > 0)
        {
          inEvent = true;
          currentEventStart = dateTimes[i];
        }
        else if (inEvent && maxValueWithinIntereventTime <= 0)
        {
          inEvent = false;
          currentEventStart = null;
        }

        if (inEvent)
          currentRunningSum.EventStart = currentEventStart;
        else
          currentRunningSum.EventStart = null;

        int k = 0;
        value = 0;
        foreach (int sumRange in sumRanges)
        {
          for (int l = k / timeStep; l < sumRange / timeStep; l++)
          {
            value += array[i + l];
          }
          currentRunningSum.Totals[sumRange] = value;
          currentRunningSum.CurrentDateTime = dt;
          k = sumRange;
        }
        runningSums.Add(dt, currentRunningSum);
        dt = dt.AddMinutes(timeStep);

      }

      IEnumerable<IGrouping<DateTime?, RunningSum>> query =
        runningSums.GroupBy(p => p.Value.EventStart, p => p.Value);

      writer.Write("Event_Start_Date,");
      foreach (int range in sumRanges)
      {
        writer.Write(range + "_Date,");
        writer.Write(range + ",");
      }
      writer.WriteLine();

      foreach (IGrouping<DateTime?, RunningSum> grouping in query)
      {
        if (!grouping.Key.HasValue)
          continue;

        writer.Write(grouping.Key.Value.ToString(@"MM/dd/yyyy HH:mm:ss") + ",");

        foreach (int range in sumRanges)
        {
          RunningSum max = grouping.First();
          double sum = max.Totals[range];
          DateTime? yo = max.CurrentDateTime;

          foreach (RunningSum rs in grouping)
          {
            if (sum < rs.Totals[range])
            {
              sum = rs.Totals[range];
              yo = rs.CurrentDateTime;
            }              
          }
          
          writer.Write(yo + "," + sum + ",");
        }
        writer.WriteLine();
      }
      reader.Close();
      writer.Close();

      return;
    }
    public void ProcessRankedMinimums(StreamReader reader, StreamWriter writer)
    {
      int timeStep = 1440;//24 hours in minutes
      int averagingPeriod = 7;

      string line;// = string.Empty;
      string[] tokens;

      DateTime dt = DateTime.MinValue;
      double value = 0;

      Dictionary<DateTime, RunningSum> runningSums = new Dictionary<DateTime, RunningSum>();
      TimeSeries<double> timeSeries = new TimeSeries<double>();
      string headerLine = reader.ReadLine();

      while ((line = reader.ReadLine()) != null)
      {
        tokens = line.Split(',');
        dt = DateTime.Parse(tokens[0]);
        value = double.Parse(tokens[1]);
        timeSeries.Add(dt, value);
      }

      /*for (int i = 0; i < averagingPeriod; i++)
      {
        dt = dt.AddMinutes(timeStep);
        timeSeries.Add(dt, timeSeries[timeSeries.Count - 1]);
      }*/

      TimeSeries<double> newTs = new TimeSeries<double>();
      dt = timeSeries.Keys.ElementAt(0);
      
      
      double sum = 0;
      int i = 0;
      foreach (KeyValuePair<DateTime, double> kvp in timeSeries)
      {        
        sum = timeSeries.Skip(i).Take(averagingPeriod).Sum(p => p.Value);
        newTs.Add(kvp.Key, sum / averagingPeriod);        
        i++;
      }

      writer.WriteLine("WaterYear,Average_" + averagingPeriod + "_Day");

      IEnumerable<IGrouping<int, double>> query = newTs.GroupBy(p => p.Key.WaterYear(), p=> p.Value);

      foreach (IGrouping<int, double> grouping in query.OrderBy(p => p.Key))
      {        
        writer.WriteLine(grouping.Key + "," + grouping.Min(p => p));
      }

      reader.Close();
      writer.Close();

      return;
    }

    public class RunningSum
    {
      public DateTime? EventStart, EventEnd, CurrentDateTime;
      public double Duration;
      public double CurrentValue;

      public IDictionary<int, double> Totals;

      public RunningSum()
      {
        Totals = new Dictionary<int, double>();
        foreach (int i in sumRanges)
          Totals.Add(i, 0);

        Duration = 0;
        CurrentValue = 0;
        this.EventStart = null;
        this.EventEnd = null; ;
      }
    }


    public void CalculateTQ05(StreamReader reader, StreamWriter writer)
    {
      double flowThreshold;
      //flowThreshold = 8.8; //value for Stephens Creek at I-5 culvert
      //flowThreshold = 17.0; //value for Stephens Creek near mouth;

      flowThreshold = 44.0; //value for Stephens Creek at I-5 culvert, existing condition

      string line;// = string.Empty;
      string[] tokens;
      double value;
      DateTime dt;

      int timeStepsAboveThreshold = 0;
      double timeStepCount = 0;
      int? year, previousYear = null;
      line = reader.ReadLine(); //header

      tokens = line.Split(',');
      string element = tokens[1].Trim();

      writer.WriteLine("WaterYear,TQ0.5_" + element);

      while ((line = reader.ReadLine()) != null)
      {
        tokens = line.Split(',');
        dt = DateTime.Parse(tokens[0]);

        year = WaterYear(dt);
        if (previousYear == null)
          previousYear = year;

        value = double.Parse(tokens[1]);

        timeStepCount++;
        if (value >= flowThreshold)
          timeStepsAboveThreshold++;

        if (year != previousYear)
        {
          writer.WriteLine(year + "," + (timeStepsAboveThreshold / timeStepCount).ToString("0.0000"));
          timeStepsAboveThreshold = 0;
          timeStepCount = 0;
        }
        previousYear = year;
      }


    }

    private int WaterYear(DateTime dt)
    {
      if (dt.Month >= 10)
        return dt.Year + 1;
      else
        return dt.Year;
    }
  }


}
